<template>
  <div class="shape" :class="{ active,locked:element.locked}" @click="selectCurComponent($event)"
    @mousedown="handleMouseDownOnShape($event)" ref="shape">
    <span v-show="isActive()" class="iconfont icon-xiangyouxuanzhuan"
      @mousedown="handleRotate($event)" @dblclick="handleResetRotate($event)"></span>
    <div v-for="item in isActive() ? pointList : []" :key="item"
      class="shape-point" :style="getPointStyle(item)"
      @mousedown="handleMouseDownOnPoint( $event,item)"></div>
    <slot></slot>
  </div>
</template>
<script>
import { computed, defineComponent, nextTick, onMounted, ref } from "vue";
import { useDesignStore } from "../../../../../../store/design";
import { emitter } from "../../../../../../utils/emitter";
import { runAnimation } from "../../../../../../utils/style";
import { mod360 } from "../../../../../../utils/translate";
import { calculateComponentPositonAndSize } from "../../../../../../utils/calculateComponentPositonAndSize";

export default defineComponent({
  name: "shape",
  props: {
    active: {
      type: Boolean,
      default: false,
    },
    element: {
      require: true,
      type: Object,
      default: () => {},
    },
    defaultStyle: {
      require: true,
      type: Object,
      default: () => {
        return {
          top: 0,
          left: 0,
          width: 0,
          height: 0,
          rotate: 0,
          opacity: 1,
          borderRadius: 0,
          borderStyle: "solid",
          borderWidth: 0,
          bgColor: "transparent",
        };
      },
    },
    index: {
      require: true,
      type: [Number, String],
      default: 0,
    },
  },
  setup(props, ctx) {
    const shape = ref();
    const designStore = useDesignStore();
    const pageConfig = computed(() => designStore.pageConfig);
    const pointList = ref(["lt", "t", "rt", "r", "rb", "b", "lb", "l"]); // 八个方向
    // 每个点对应的初始角度
    const initialAngle = ref({
      lt: 0,
      t: 45,
      rt: 90,
      r: 135,
      rb: 180,
      b: 225,
      lb: 270,
      l: 315,
    });
    // 每个范围的角度对应的光标
    const angleToCursor = ref([
      { start: 338, end: 23, cursor: "nw" },
      { start: 23, end: 68, cursor: "n" },
      { start: 68, end: 113, cursor: "ne" },
      { start: 113, end: 158, cursor: "e" },
      { start: 158, end: 203, cursor: "se" },
      { start: 203, end: 248, cursor: "s" },
      { start: 248, end: 293, cursor: "sw" },
      { start: 293, end: 338, cursor: "w" },
    ]);
    const cursors = ref({});
    const curComponent = computed(() => designStore.curComponent);
    const editor = computed(() => designStore.editor);
    const selectCurComponent = (e) => {
      e.stopPropagation();
      e.preventDefault();
      designStore.hideContextMenu();
    };

    const isActive = () => {
      return props.active && !props.element.locked;
    };

    const handleMouseDownOnShape = (e) => {
      e.preventDefault();
      e.stopPropagation();
      designStore.setInEditorStatus(true);
      if (props.element.locked || props.element.hided) {
        return;
      }
      designStore.setClickComponentStatus(true);
      designStore.setCurComponent({
        component: props.element,
        index: props.index,
      });
      cursors.value = getCursor();
      const pos = { ...props.defaultStyle };
      const startY = e.clientY;
      const startX = e.clientX;
      // 如果直接修改属性，值的类型会变为字符串，所以要转为数值型
      const startTop = Number(pos.top);
      const startLeft = Number(pos.left);
      // 如果元素没有移动，则不保存快照
      let hasMove = false;
      const move = (moveEvent) => {
        hasMove = true;
        const curX = moveEvent.clientX;
        const curY = moveEvent.clientY;
        const moveX = (curX - startX) / pageConfig.value.scale + startLeft;
        const moveY = (curY - startY) / pageConfig.value.scale + startTop;
        pos.top = moveY < 0 ? 0 : moveY;
        pos.left = moveX < 0 ? 0 : moveX;
        if (pos.top + pos.height > pageConfig.value.height) {
          pos.top = pageConfig.value.height - pos.height;
        }
        if (pos.left + pos.width > pageConfig.value.width) {
          pos.left = pageConfig.value.width - pos.width;
        }

        // 修改当前组件样式
        designStore.setShapeStyle(pos);
        // 等更新完当前组件的样式并绘制到屏幕后再判断是否需要吸附
        // 如果不使用 $nextTick，吸附后将无法移动
        nextTick(() => {
          // 触发元素移动事件，用于显示标线、吸附功能
          // 后面两个参数代表鼠标移动方向
          // curY - startY > 0 true 表示向下移动 false 表示向上移动
          // curX - startX > 0 true 表示向右移动 false 表示向左移动
          emitter.on("move", {
            y: (curY - startY) / pageConfig.value.scale > 0,
            x: (curX - startX) / pageConfig.value.scale > 0,
          });
        });
      };

      const up = () => {
        if (hasMove) {
          designStore.addSnapshot();
        }
        // 触发元素停止移动事件，用于隐藏标线
        emitter.on("unmove");
        document.removeEventListener("mousemove", move);
        document.removeEventListener("mouseup", up);
      };

      document.addEventListener("mousemove", move);
      document.addEventListener("mouseup", up);
    };

    const handleRotate = (e) => {
      designStore.setClickComponentStatus(true);
      e.preventDefault();
      e.stopPropagation();

      // 初始坐标和初始角度
      const pos = { ...props.defaultStyle };
      const startX = e.clientX;
      const startY = e.clientY;
      const startRotate = pos.rotate;

      // 获取元素中心点位置
      const rect = shape.value.getBoundingClientRect();
      const centerX = rect.left + rect.width / 2;
      const centerY = rect.top + rect.height / 2;
      // 旋转前的角度
      const rotateDegreeBefore =
        Math.atan2(startY - centerY, startX - centerX) / (Math.PI / 180);
      // 如果元素没有移动，则不保存快照
      let hasMove = false;
      const move = (moveEvent) => {
        hasMove = true;
        const curX = moveEvent.clientX;
        const curY = moveEvent.clientY;
        // 旋转后的角度
        const rotateDegreeAfter =
          Math.atan2(curY - centerY, curX - centerX) / (Math.PI / 180);
        // 获取旋转的角度值
        pos.rotate = startRotate + rotateDegreeAfter - rotateDegreeBefore;
        // 修改当前组件样式
        designStore.setShapeStyle(pos);
      };
      const up = () => {
        hasMove && designStore.addSnapshot();
        document.removeEventListener("mousemove", move);
        document.removeEventListener("mouseup", up);
        cursors.value = getCursor(); // 根据旋转角度获取光标位置
      };
      document.addEventListener("mousemove", move);
      document.addEventListener("mouseup", up);
    };

    const handleResetRotate=(e)=>{
      designStore.setClickComponentStatus(true);
      e.preventDefault();
      e.stopPropagation();
      const pos = { ...props.defaultStyle };
      pos.rotate = 0;
      designStore.setShapeStyle(pos);
    }

    const getCursor = () => {
      if (!curComponent.value || !curComponent.value.id) {
        return;
      }
      const rotate = mod360(curComponent.value.attr.rotate);
      const resulte = {};
      let lastMatchIndex = -1;
      pointList.value.forEach((point) => {
        const angle = mod360(initialAngle.value[point] + rotate);
        const len = angleToCursor.value.length;
        while (true) {
          lastMatchIndex = (lastMatchIndex + 1) % len;
          const angleLimit = angleToCursor.value[lastMatchIndex];
          if (angle < 23 || angle >= 338) {
            resulte[point] = "nw-resize";
            return;
          }
          if (angleLimit.start <= angle && angle < angleLimit.end) {
            resulte[point] = angleLimit.cursor + "-resize";
            return;
          }
        }
      });
      return resulte;
    };

    const getPointStyle = (point) => {
      const { width, height } = props.defaultStyle;
      const hasT = /t/.test(point);
      const hasB = /b/.test(point);
      const hasL = /l/.test(point);
      const hasR = /r/.test(point);
      let newLeft = 0;
      let newTop = 0;
      if (point.length === 2) {
        newLeft = hasL ? 0 : width;
        newTop = hasT ? 0 : height;
      } else {
        if (hasT || hasB) {
          newLeft = width / 2;
          newTop = hasT ? 0 : height;
        }

        // 左右两边的点，高度居中
        if (hasL || hasR) {
          newLeft = hasL ? 0 : width;
          newTop = Math.floor(height / 2);
        }
      }
      const style = {
        marginLeft: "-4px",
        marginTop: "-4px",
        left: `${newLeft}px`,
        top: `${newTop}px`,
        cursor: cursors.value[point],
        transform: `scale(1 / ${pageConfig.value.scale}) translate(0px, 0px)`,
      };

      return style;
    };
    const isNeedLockProportion = () => {
      if (props.element.type != "group") {
        return false;
      }
      const ratates = [0, 90, 180, 360];
      for (const element of props.element.children) {
        if (!ratates.includes(mod360(parseInt(component.attr.rotate)))) {
          return true;
        }
      }
    };
    const handleMouseDownOnPoint = (e, point) => {
      designStore.setInEditorStatus(true);
      e.stopPropagation();
      e.preventDefault();
      designStore.setCurComponent({
        component: props.element,
        index: props.index,
      });
      designStore.setClickComponentStatus(true);
      const style = { ...props.defaultStyle };
      const proportion = style.width / style.height;
      const center = {
        x: style.left + style.width / 2,
        y: style.top + style.height / 2,
      };

      // 获取画布位移信息
      const editorRectInfo = editor.value.getBoundingClientRect();
      // 获取 point 与实际拖动基准点的差值
      const pointRect = e.target.getBoundingClientRect();
      // 当前点击圆点相对于画布的中心坐标
      const curPoint = {
        x: Math.round(
          (pointRect.left - editorRectInfo.left) / pageConfig.value.scale +
            e.target.offsetWidth / 2
        ),
        y: Math.round(
          (pointRect.top - editorRectInfo.top) / pageConfig.value.scale +
            e.target.offsetHeight / 2
        ),
      };
      // 获取对称点的坐标
      const symmetricPoint = {
        x: center.x - (curPoint.x - center.x),
        y: center.y - (curPoint.y - center.y),
      };
      // 是否需要保存快照
      let needSave = false;
      let isFirst = true;
      const needLockProportion = isNeedLockProportion();
      const move = (moveEvent) => {
        // 第一次点击时也会触发 move，所以会有“刚点击组件但未移动，组件的大小却改变了”的情况发生
        // 因此第一次点击时不触发 move 事件
        if (isFirst) {
          isFirst = false;
          return;
        }
        needSave = true;
        const curPositon = {
          x: (moveEvent.clientX - editorRectInfo.left) / pageConfig.value.scale,
          y: (moveEvent.clientY - editorRectInfo.top) / pageConfig.value.scale,
        };
        calculateComponentPositonAndSize(
          point,
          style,
          curPositon,
          proportion,
          needLockProportion,
          {
            center,
            curPoint,
            // 对称点的坐标
            symmetricPoint,
          }
        );
        designStore.setShapeStyle(style);
      };
      const up = () => {
        document.removeEventListener("mousemove", move);
        document.removeEventListener("mouseup", up);
        needSave && designStore.addSnapshot();
      };
      document.addEventListener("mousemove", move);
      document.addEventListener("mouseup", up);
    };
    onMounted(() => {
      if (curComponent.value && curComponent.value.id) {
        cursors.value = getCursor();
      }
      emitter.on("runAnimation", () => {
        if (element.value == curComponent.value) {
          runAnimation(shape.value, curComponent.value.animation);
        }
      });
      emitter.on("stopAnimation", () => {
        shape.value.classList.remove("animated", "infinite");
      });
    });

    return {
      shape,
      editor,
      pointList,
      pageConfig,
      initialAngle,
      angleToCursor,
      cursors,
      curComponent,
      getCursor,
      selectCurComponent,
      isActive,
      handleRotate,
      handleResetRotate,
      handleMouseDownOnShape,
      getPointStyle,
      handleMouseDownOnPoint,
      isNeedLockProportion,
    };
  },
});
</script>
<style lang="less">
.active {
  outline: 1px solid #2483ff;
  user-select: none;
}
.shape {
  position: absolute;
  &:hover {
    cursor: move;
  }
}
.shape.locked{
  &:hover {
    cursor: default;
  }
}
.shape-point {
  position: absolute;
  background: #fff;
  border: 1px solid #59c7f9;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  z-index: 1;
}

.icon-xiangyouxuanzhuan {
  position: absolute;
  top: -34px;
  left: 50%;
  transform: translateX(-50%);
  cursor: grab;
  color: #59c7f9;
  font-size: 20px;
  font-weight: 600;
  &:active {
    cursor: grabbing;
  }
}
</style>
